#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#define CRW_IMPL
#include "parStr.h"
#include "error.h"

struct sparStr {
  char * fst, *snd;
  size_t lf, ls;
};


parStr
parStr_create(const char * fst, const char * snd){

/*
  DESC: Constructor. Toma 2 null terminated strings.
  No los usa.
  Devuelve NULL si no hay memoria.
  PRE: { e = NULL /\ fst -> S1 /\ snd -> S2 }
  e = parStr_create(S1,S2);  
  POS: {  Hay memoria ==>  e --> (S1,S2)
  ! Hay memoria ==>  e = NULL  }
*/
  parStr e;
  size_t lf, ls;

  lf = strnlen(fst,SIZE_MAX);
  ls = strnlen(snd,SIZE_MAX);

  assert( lf < SIZE_MAX);
  assert( ls < SIZE_MAX);

  e = (parStr) malloc(sizeof(struct sparStr));

  if (e != NULL){

    e->lf = lf; 
    e->ls = ls; 
    e->fst = (char *) calloc(lf+1,sizeof(char));

    if (e->fst == NULL){
      free(e);
      e = NULL;
    } else {

      e->snd = (char *) calloc(ls+1,sizeof(char));

      if (e->snd == NULL){
	free(e->fst);
	e->fst = NULL;
	free(e);
	e = NULL;
      } else {
	strcpy(e->fst,fst);
	strcpy(e->snd,snd);
      }
    }
  }
  return e;
}



parStr
parStr_destroy(parStr e){
/*
DESC: Destructor del tipo. e cambia.
PRE: { e --> E }
   e = parStr_destroy(e);
POS: { Se destruye E /\ e == NULL }
*/
  assert(e != NULL);
  assert(e->lf >= 0);
  assert(e->ls >= 0);
  assert(e->fst != NULL);
  assert(e->snd != NULL);

  free(e->fst);
  e->fst = NULL;
  free(e->snd);
  e->snd = NULL;

  e->lf = 0;
  e->ls = 0;
  free(e);
  e = NULL;

  return e;
}


char *
parStr_fst(const parStr e){
/*
DESC: Devuelve un nuevo string (despues de usarlo hay que liberarlo) que 
      representa el primer elemento de parStr.
PRE: { e --> E /\ s = NULL }
        s = parStr_fst(e);
POS: {   Hay memoria ==> s --> toStr(fst(E)) 
       ! Hay memoria ==> s == NULL }
*/
  char *res;

  assert(e != NULL);
  assert(e->lf >= 0);
  assert(e->ls >= 0);
  assert(e->fst != NULL);
  assert(e->snd != NULL);

  res = (char *) calloc(e->lf + 1,sizeof(char));
  if (res != NULL)
    strcpy(res,e->fst);

  return res;
}

char *
parStr_snd(const parStr e){
/*
DESC: Devuelve un nuevo string (despues de usarlo hay que liberarlo) que 
      representa el segundo elemento de parStr.
PRE: { e --> E /\ s = NULL }
        s = parStr_snd(e);
POS: {   Hay memoria ==> s --> toStr(snd(E)) 
       ! Hay memoria ==> s == NULL }
*/
  char *res;

  assert(e != NULL);
  assert(e->lf >= 0);
  assert(e->ls >= 0);
  assert(e->fst != NULL);
  assert(e->snd != NULL);

  res = (char *) calloc(e->ls + 1,sizeof(char));
  if (res != NULL)
    strcpy(res,e->snd);

  return res;
}


parStr 
parStr_clone(const parStr e){
/*
DESC: Construye una copia.
PRE: { e1 --> E1 /\ e2 = NULL }
   e2 = parStr_clone(e1);  
POS: {  Hay memoria ==> e1 --> E1 /\ e2 --> E1 /\ e1 != e2
      ! Hay memoria ==> e2 == NULL /\ e1 --> E1 }
*/
  assert(e != NULL);
  assert(e->lf >= 0);
  assert(e->ls >= 0);
  assert(e->fst != NULL);
  assert(e->snd != NULL);

  return parStr_create(e->fst, e->snd);
}


char *
readUntilDelim(FILE * f, char del, size_t *len){
  size_t aux;
  char * res;

  res = NULL;
  aux = 0;
  *len = getdelim (&res, &aux, del, f);

  /* Todos estos test son por que puede variar la repr. de size_t */
  if (*len != -1 && *len != 0 && *len > 0 && 
      (res[*len - 1] == del || feof(f))){
    if (res[*len - 1] == del) {
      *len = *len - 1;
      res[*len] = '\0';
    }
  } else {
    if (aux > 0)
      free(res);
    res = NULL;
    *len = 0;
  }

  return res;
}

parStr
parStr_fromStream(FILE * f){
/*
DESC: Constructor. Lee un parStror desde un stream.
      Devuelve NULL si no hay memoria o hubo error de archivo
      (error de hardware o parseo).
PRE: { e = NULL /\ f -> F }
  e = parStr_fromStream(f);
POS: {  Hay memoria /\ ! Error de Archivo ==>  e --> fromStream(F)
      ! Hay memoria \/  Error de Archivo  ==>  e = NULL  }
*/

  parStr e;
  static int lineNum = 0;

  assert(f != NULL);

  lineNum = lineNum + 1;

  e = (parStr) malloc(sizeof(struct sparStr));

  if (e != NULL){
    e->fst = readUntilDelim(f, ':', &(e->lf));

    if (e->fst != NULL){

      e->snd = readUntilDelim(f, '\n', &(e->ls));

      if (e->snd == NULL){ /* (error parseo) */
	warnx(ERRFMT,"parStr_fromStream, error parseo, segundo campo",lineNum);
	free(e->fst);
	e->fst = NULL;
	e->lf = 0;
	free(e);
	e = NULL;
      }

    } else { /* e->fst == NULL (error parseo) */
	warnx(ERRFMT,"parStr_fromStream, error parseo, segundo campo",lineNum);
	free(e);
	e = NULL;
    }
  }
  
  return e;
}

bool
parStr_toStream(FILE *f, const parStr e){
/*
DESC: Guarda el parStr en un stream. 
PRE: { e -> E /\ f -> F }
  b = parStr_toStream(f,e);  
POS: { ! Error de Archivo ==> b /\ f -> add(F,e) 
         Error de Archivo ==> !b }   
*/
  assert(f != NULL);
  assert(e != NULL);
  assert(e->lf >= 0);
  assert(e->ls >= 0);
  assert(e->fst != NULL);
  assert(e->snd != NULL);

  return (fprintf(f,"%s:%s\n",e->fst,e->snd) >= 0);

}
